#include "transfile_server.h"

#include "struct_tlv.h"

#include "log.h"

int gets_server(userOpInfo_t *userOpInfo) {
	tlvPacket_t packet;
	memset(&packet, 0, sizeof(packet));

	// //获取已传文件字节数
	// recvn(userOpInfo->netfd,(char*)&packet.type,sizeof(packet.type));
	// int exist_size = packet.type;

	// 接受文件名
	recv(userOpInfo->netfd, (char*)&packet.type, sizeof(packet.type), MSG_WAITALL);
	char filename[1024] = {0};
	recv(userOpInfo->netfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
	recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
	memcpy(filename, packet.value, packet.length);

	// 判断文件名对应的以hash值为文件名的文件是否存在
	char sql[4096] = {0};
	sprintf(sql, "select hash from file_info where filename = '%s' and username = '%s' and parent_id = %d;",
	        filename, userOpInfo->userName, userOpInfo->ID);

	mysql_query(userOpInfo->mysql, sql);
	MYSQL_RES *result = mysql_store_result(userOpInfo->mysql);
	MYSQL_ROW row = mysql_fetch_row(result);
	char hash[41] = {0};
	memcpy(hash, row[0], strlen(row[0]));

	// 拼接真实路径
	char workPath[4096] = {0};
	strcat(workPath, userOpInfo->addr);
	strcat(workPath, "/");
	strcat(workPath, hash);

	//对文件是否存在进行错误处理
	int error_flag = 1;
	if (access(workPath, F_OK) != 0) {
		error_flag = 0;
		send(userOpInfo->netfd, &error_flag, sizeof(error_flag), MSG_NOSIGNAL);
		printf("\n");
		return -1;
	}
	send(userOpInfo->netfd, &error_flag, sizeof(error_flag), MSG_NOSIGNAL);

	//打开需下载文件的fd
	int fd = open(workPath, O_RDONLY);
	ERROR_CHECK(fd, -1, "open");

    // //判断文件已传大小
    // if(exist_size > 0){
    //     lseek(fd,exist_size,SEEK_SET);
    // }

	// 获取发送文件大小
	struct stat statbuf;
	fstat(fd, &statbuf);
	off_t filesize = statbuf.st_size;

	// 发送文件大小
	memset(&packet, 0, sizeof(packet));
	packet.type = GETS;
	packet.length = sizeof(filesize);
	memcpy(packet.value, &filesize, packet.length);
	send(userOpInfo->netfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);

	// 若传输文件大于100M, 采用mmap
	if (filesize > 104857600) {
        // size_t page_size = sysconf(_SC_PAGESIZE);
        // off_t aligned_exist_size =(exist_size / page_size)*page_size;
        // off_t offset_in_page = exist_size - aligned_exist_size;
        // //实际映射长度
        // size_t filesize = filesize - aligned_exist_size;

		ftruncate(fd, filesize);  // 处理磁盘上的空间
		char *p = (char *)mmap(NULL, filesize, PROT_READ, MAP_SHARED, fd, 0);  // 处理进程虚拟内存到内核缓冲区的映射
		send(userOpInfo->netfd, p, filesize, MSG_NOSIGNAL);
		munmap(p, filesize);
	} else {
		while (1) {
			memset(&packet, 0, sizeof(packet));
			packet.type = GETS;
			packet.length = read(fd, packet.value, sizeof(packet.value));
			if (packet.length > 0) {
				send(userOpInfo->netfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);
			} else {
				break;
			}
		}

		// 发送结束标志
		memset(&packet, 0, sizeof(packet));
		packet.type = GETS;
		send(userOpInfo->netfd, (char *)&packet, sizeof(packet.type) + sizeof(packet.length) + packet.length, MSG_NOSIGNAL);
	}

	close(fd);

	printf("send complete\n");
	return 1;
}

int puts_server(userOpInfo_t *userOpInfo) {
	tlvPacket_t packet;
	int ifExist = 1; // 文件是否存在
	int isQuickUpload = 0; // 新增标志：是否是秒传

	// 接收文件是否存在标志
	recv(userOpInfo->netfd, &ifExist, sizeof(ifExist), MSG_WAITALL);
	if (ifExist == -1) {
		printf("filename error, please input again!\n");
		return -1;
	}

	// 接收文件hash
	char hash[41] = {0};
	memset(&packet, 0, sizeof(packet));
	recv(userOpInfo->netfd, &packet.type, sizeof(packet.type), MSG_WAITALL);
	recv(userOpInfo->netfd, &packet.length, sizeof(packet.length), MSG_WAITALL);
	recv(userOpInfo->netfd, hash, packet.length, MSG_WAITALL);

	memset(&packet, 0, sizeof(packet));
	// 接收文件名
	char filename[1024] = {0};
	recv(userOpInfo->netfd, &packet.type, sizeof(packet.type), MSG_WAITALL);
	recv(userOpInfo->netfd, &packet.length, sizeof(packet.length), MSG_WAITALL);
	recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
	memcpy(filename, packet.value, packet.length);

	// 拼接真实存储路径（基于hash）
	char workPath[1024] = {0};
	strcat(workPath, userOpInfo->addr);
	strcat(workPath, "/");
	strcat(workPath, hash);

	// 检查是否已存在完全相同的文件（相同hash、相同username、相同parentID、相同filename）
	char sql[4096] = {0};
	sprintf(sql, "SELECT * FROM file_info WHERE hash = '%s' AND username = '%s' AND parent_id = %d AND filename = '%s';",
	        hash, userOpInfo->userName, userOpInfo->ID, filename);
	mysql_query(userOpInfo->mysql, sql);
	MYSQL_RES *result = mysql_store_result(userOpInfo->mysql);
	MYSQL_ROW row = mysql_fetch_row(result);

	if (row != NULL) {
		// 文件已存在（完全相同的记录），直接返回成功
		int transFlag = 2;
		send(userOpInfo->netfd, &transFlag, sizeof(transFlag), MSG_NOSIGNAL);
		printf("File already exists, no changes made.\n");
		mysql_free_result(result);
		return 1;
	}

	// 检查文件内容是否已存在（仅hash相同）
	int existingQuotenum = 1;
	memset(sql, 0, sizeof(sql));
	sprintf(sql, "SELECT quotenum FROM file_info WHERE hash = '%s' LIMIT 1;", hash);
	mysql_query(userOpInfo->mysql, sql);
	result = mysql_store_result(userOpInfo->mysql);
	row = mysql_fetch_row(result);

	if (row != NULL) {
		// 文件内容已存在（hash相同但路径不同），允许秒传
		isQuickUpload = 1; // 设置秒传标志
		existingQuotenum = atoi(row[0]);
		int transFlag = 1;
		send(userOpInfo->netfd, &transFlag, sizeof(transFlag), MSG_NOSIGNAL);
	} else {
		// 文件内容不存在，需要完整传输
		int transFlag = 0;
		send(userOpInfo->netfd, &transFlag, sizeof(transFlag), MSG_NOSIGNAL);

		// 打开文件并接收内容
		int fd = open(workPath, O_RDWR | O_CREAT | O_TRUNC, 0666);
		ERROR_CHECK(fd, -1, "open");

		// 接收文件大小
		off_t filesize;
		memset(&packet, 0, sizeof(packet));
		recv(userOpInfo->netfd, (char *)&packet.type, sizeof(packet.type), MSG_WAITALL);
		recv(userOpInfo->netfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
		recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
		filesize = *((int *)packet.value);
		memcpy(&filesize, packet.value, packet.length);

		// 传输文件内容（mmap或分块传输）
		if (filesize > 104857600) {
			ftruncate(fd, filesize);
			char *p = (char *)mmap(NULL, filesize, PROT_WRITE, MAP_SHARED, fd, 0);
			recv(userOpInfo->netfd, p, filesize, MSG_WAITALL);
			munmap(p, filesize);
		} else {
			while (1) {
				memset(&packet, 0, sizeof(packet));
				recv(userOpInfo->netfd, (char *)&packet.type, sizeof(packet.type), MSG_WAITALL);
				recv(userOpInfo->netfd, (char *)&packet.length, sizeof(packet.length), MSG_WAITALL);
				if (packet.length > 0) {
					recv(userOpInfo->netfd, packet.value, packet.length, MSG_WAITALL);
					write(fd, packet.value, packet.length);
				} else {
					break;
				}
			}
		}
		close(fd);
	}

	// 插入新记录
	char *userName = userOpInfo->userName;
	int parentID = userOpInfo->ID;
	char type = 'f';
	char bufSql[4096] = {0};

	// 拼接path
	sprintf(bufSql, "SELECT path FROM file_info WHERE id = %d;", userOpInfo->ID);
	mysql_query(userOpInfo->mysql, bufSql);
	result = mysql_store_result(userOpInfo->mysql);
	row = mysql_fetch_row(result);
	char curPath[1024] = {0};
	if (row != NULL) {
		memcpy(curPath, row[0], strlen(row[0]));
		strcat(curPath, filename);
	}

	// 插入数据库（quotenum=existingQuotenum）
	memset(bufSql, 0, sizeof(bufSql));
	sprintf(bufSql, "INSERT INTO file_info (filename, username, parent_id, hash, type, path, quotenum) "
	                "VALUES ('%s', '%s', %d, '%s', '%c', '%s', %d);",
	        filename, userName, parentID, hash, type, curPath, existingQuotenum);
	mysql_query(userOpInfo->mysql, bufSql);

	// 如果是秒传，更新所有相同hash记录的quotenum（包括刚插入的）
	if (isQuickUpload) {
		memset(bufSql, 0, sizeof(bufSql));
		sprintf(bufSql, "UPDATE file_info SET quotenum = quotenum + 1 WHERE hash = '%s';", hash);
		mysql_query(userOpInfo->mysql, bufSql);
	}

	printf("File upload complete.\n");
	if (result) mysql_free_result(result);
	return 1;
}
